<!--
 * @Description:文件上传
 * @Autor: HuiSir<273250950@qq.com>
 * @Date: 2025-03-28 17:37:21
 * @LastEditTime: 2025-04-02 09:37:01
-->
<template>
  <el-upload
    :file-list="fileList"
    :action="action.url"
    :data="{ type: uploadType }"
    :headers="{ Authorization: 'Bearer ' + getToken() }"
    :list-type="isPic ? 'picture-card' : 'text'"
    :multiple="multiple"
    :on-preview="handlePictureCardPreview"
    :on-remove="handleRemove"
    :before-upload="beforeAvatarUpload"
    :on-success="successHandle"
    :limit="limit"
    :on-exceed="
      () => {
        ElMessage.warning(`此处只能上传${limit}个文件`);
      }
    "
  >
    <el-icon v-if="isPic">
      <Plus />
    </el-icon>
    <el-button v-else :icon="Upload">上传文件</el-button>
    <template v-if="tip" #tip>
      <div class="el-upload__tip">
        {{ tip }}
      </div>
    </template>
  </el-upload>
  <el-dialog v-model="previewVisible">
    <img :src="previewImageUrl" alt="Preview Image" style="width: 600px; height: auto" />
  </el-dialog>
</template>
<script lang="ts" setup>
import { ref, Ref, computed, PropType } from "vue";
import { ElMessage } from "element-plus";
import { action, isImage, ossUrl } from "@/utils/tools";
import { getToken } from "@/utils/cache";
import type { UploadProps } from "element-plus";
import { Upload } from "@element-plus/icons-vue";

const props = defineProps({
  // 原表单数据
  modelValue: {
    type: String,
    require: true
  },
  // 数量限制
  limit: {
    type: Number,
    require: false,
    default: 1
  },
  // 是否多选
  multiple: {
    type: Boolean,
    require: false,
    default: false
  },
  // 是否图片
  isPic: {
    type: Boolean,
    require: false,
    default: true
  },
  // 扩展名
  extName: {
    type: Array as PropType<string[]>,
    require: false,
    default: null
  },
  // 提示
  tip: {
    type: String,
    require: false,
    default: ""
  },
  // 上传服务器类型
  uploadType: {
    type: String as PropType<"common" | "svc" | "user">,
    require: false,
    default: "common"
  }
});
const emit = defineEmits(["update:modelValue", "success", "remove"]);

// 默认图片列表
const fileList = computed(() => (props.modelValue ? props.modelValue.split(",").map((url) => ({ url: ossUrl + url, name: url.slice(url.lastIndexOf("/") + 1) })) : []));
const uploadList: Ref<string[] | any> = ref([]);
const previewVisible = ref(false);
const previewImageUrl = ref("");

// 删除
const handleRemove: UploadProps["onRemove"] = (uploadFile: any) => {
  const removeURL = uploadFile?.response?.data?.url || uploadFile.url;
  const fileList = uploadList.value.filter((item: string) => item !== removeURL && !removeURL.includes(item));
  emit("remove", removeURL);
  emit("success", fileList);
  emit("update:modelValue", fileList.join(","));
};

// 预览
const handlePictureCardPreview: UploadProps["onPreview"] = (uploadFile: any) => {
  if (props.isPic) {
    previewImageUrl.value = uploadFile.url;
    previewVisible.value = true;
  } else {
    window.open(uploadFile.url);
  }
};
// 上传前
const beforeAvatarUpload: UploadProps["beforeUpload"] = (rawFile: Record<any, any>) => {
  if (props.extName) {
    if (!props.extName.some((ext: string) => rawFile.name.endsWith(ext))) {
      ElMessage.error(`请上传扩展名为${props.extName.join("、")}格式的文件`);
      return false;
    }
  } else if (props.isPic) {
    let type = isImage(rawFile.name);
    if (!type) {
      ElMessage.error("请上传正确格式");
      return false;
    }
  }
  if (rawFile.size / 1024 / 1024 > 3) {
    ElMessage.error("请上传大小为3M以内的文件");
    return false;
  }
  return true;
};
// 上传完成
const successHandle = (res: Record<any, any>, file: Record<any, any>, list: Record<any, any>[]) => {
  if (res.code !== 0) {
    return ElMessage.error(res.msg);
  }
  uploadList.value = list.map((item) => item.response.data.url);
  emit("success", uploadList.value, res.data.url);
  emit("update:modelValue", uploadList.value.join(","));
};
</script>
<style scoped lang="less">
:deep(.el-upload-list__item) {
  background-color: var(--el-fill-color-light) !important;
  .el-upload-list__item-info {
    width: 350px !important;
  }
}
:deep(.el-upload__tip) {
  color: #a8a8a8 !important;
  margin-top: 0 !important;
}
</style>
